package com.easylinkin.linkappapi.security.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappPrivilege;
import com.easylinkin.linkappapi.security.entity.LinkappRole;
import com.easylinkin.linkappapi.security.entity.LinkappRoleRefSpace;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.security.entity.LinkappUserRefRole;
import com.easylinkin.linkappapi.security.mapper.LinkappPrivilegeMapper;
import com.easylinkin.linkappapi.security.mapper.LinkappRoleMapper;
import com.easylinkin.linkappapi.security.repository.LinkappPrivilegeRepository;
import com.easylinkin.linkappapi.security.repository.LinkappRoleRepository;
import com.easylinkin.linkappapi.security.service.LinkappRoleService;
import com.easylinkin.linkappapi.space.entity.LinkappSpace;
import com.easylinkin.sm.constant.LogConstant;
import com.easylinkin.sm.util.DataPermissionUtils;

import lombok.extern.slf4j.Slf4j;
import site.morn.boot.data.DisplayableServiceSupport;
import site.morn.boot.jpa.SpecificationBuilder;
import site.morn.core.CriteriaMap;
import site.morn.framework.context.AccountContext;
import site.morn.framework.context.dto.BaseLoginInfo;
import site.morn.framework.entity.BaseRole;
import site.morn.log.OperateArguments;
import site.morn.rest.RestModel;

/**
 * 角色服务实现
 *
 * @author liuming
 */
@Slf4j
@Service
@Transactional
public class LinkappRoleServiceImpl extends DisplayableServiceSupport<LinkappRole, Long, LinkappRoleRepository>
		implements LinkappRoleService {

	@Autowired
	LinkappPrivilegeRepository privilegeRepository;
	
	@Autowired
	LinkappPrivilegeMapper privilegeMapper;

	@Autowired
	LinkappRoleRepository roleRepository;

	@Resource
	private LinkappRoleMapper linkappRoleMapper;

	@Autowired
	LinkappUserContextProducer linkappUserContextProducer;

	@Override
	public Collection<LinkappRole> getByUserId(Long userId) {
		List<LinkappRole> roles = linkappRoleMapper.findByUsersId(userId);
		return roles;
	}

	@Override
	public LinkappRole get(Long id) {
		LinkappRole role = super.get(id);

		Set<LinkappPrivilege> privilegeList = new HashSet();
		role.getPrivileges().stream().forEach(privilegeList::add);
		role.setPrivileges(privilegeList);
		return role;
	}

	@Override
	public <S extends LinkappRole> S add(RestModel<S> restModel) {
		return this.addOrUpdate(restModel);
	}

	@Override
	public <S extends LinkappRole> S update(RestModel<S> restModel) {
		return this.addOrUpdate(restModel);
	}

	private <S extends LinkappRole> S addOrUpdate(RestModel<S> restModel) {
		S role = restModel.getModel();
		role.setDisplay(true);
		this.setPrivileges(role, restModel.getAttach());
		repository().save(role);
		OperateArguments.add(role.getName());
		return role;
	}

	private void setPrivileges(LinkappRole role, CriteriaMap criteriaMap) {
		List<LinkappPrivilege> privilegeList = privilegeMapper.selectPrivilegeByRole(role.getId() + "", 0);
		Set<LinkappPrivilege> privileges = new HashSet<>();
		privileges.addAll(privilegeList);
		role.setPrivileges(privileges);
	}

	@Override
	protected Specification<LinkappRole> searchSpecification(LinkappRole model, CriteriaMap attach) {
		List<Long> roleIds = this.getCurrentRoleIds();
		return SpecificationBuilder.withParameter(model, attach).specification((reference, predicate, condition) -> {
			Root root = reference.root();
			CriteriaBuilder builder = reference.builder();
			reference.query().orderBy(builder.desc(root.get(LogConstant.Fields.MODIFYTIME)));
			predicate.appendAnd(condition.contain(BaseRole.Fields.name));
			if (!roleIds.isEmpty()) {
				predicate.appendAnd(reference.path().get("id").in(roleIds).not()); // 过滤当前用户所属角色
			}
		}).and(DataPermissionUtils.subOrganizations(model.getDepartmentId())).and(DataPermissionUtils.display());
	}

	/**
	 * 获取当前登录用户的角色IDS
	 * 
	 * @return
	 */
	private List<Long> getCurrentRoleIds() {
		List<Long> roleIds = new ArrayList();
		BaseLoginInfo baseLoginInfo = AccountContext.loginInfo();
		List<LinkappRole> roles = (List<LinkappRole>) baseLoginInfo.getRoles();
		if (null != roles && !roles.isEmpty()) {
			roleIds = roles.stream().map(LinkappRole::getId).collect(Collectors.toList());
		}
		return roleIds;
	}

	@Override
	public List<LinkappRole> findRoles(LinkappRole role) {
		return linkappRoleMapper.findRoles(role);
	}

	@Override
	public IPage<LinkappRole> selectPage(Page page, LinkappRole role) {
		String tenantId = linkappUserContextProducer.getCurrent().getTenantId();
		role.setTenantId(tenantId);
		List<LinkappRole> list = linkappRoleMapper.findRoles(page, role);
		page.setRecords(list);
		return page;
	}

	@Override
	public void role2Spaces(LinkappRole role, List<LinkappSpace> spaces) {
		String tenatnId = linkappUserContextProducer.getCurrent().getTenantId();
		linkappRoleMapper.deleteRole2Spaces(role.getId());
		for (LinkappSpace space : spaces) {
			LinkappRoleRefSpace roleRefSpace = new LinkappRoleRefSpace();
			roleRefSpace.setSpaceId(space.getId().toString());
			roleRefSpace.setTenantId(tenatnId);
			roleRefSpace.setRoleId(role.getId().toString());
			roleRefSpace.setId(UUID.randomUUID().toString().replaceAll("-", ""));
			linkappRoleMapper.insertRole2Spaces(roleRefSpace);
		}
		// 修改角色空间权限的时候更新这个缓存
		AccountContext.cacheGroup().clearGroup("space");
	}

	@Override
	public List<LinkappSpace> selectSpaceByRole(Long id) {
		return linkappRoleMapper.selectSpaceByRole(id);
	}

	@Override
	public void role2Users(LinkappRole role, List<LinkappUser> users) {
		String tenatnId = linkappUserContextProducer.getCurrent().getTenantId();
		linkappRoleMapper.deleteRole2Users(role.getId());
		for (LinkappUser user : users) {
			LinkappUserRefRole userRefRole = new LinkappUserRefRole();
			userRefRole.setUserId(user.getId().toString());
			userRefRole.setTenantId(tenatnId);
			userRefRole.setRoleId(role.getId().toString());
			userRefRole.setId(UUID.randomUUID().toString().replaceAll("-", ""));
			linkappRoleMapper.insertRole2Users(userRefRole);
		}
	}

	@Override
	public List<LinkappUser> selectUserByRole(Long roleId) {
		return linkappRoleMapper.selectUserByRole(roleId);
	}

	@Override
	public List<LinkappRole> checkRole(LinkappRole role) {
		return linkappRoleMapper.findRoles(role);
	}

	@Override
	public void delete(Long roleId) {
		linkappRoleMapper.deleteRole2Users(roleId);
		super.delete(roleId);
	}

	@Override
	public LinkappRole findByRoleId(Long roldId) {
		return linkappRoleMapper.findByRoleId(roldId);
	}

	@Override
	public List<LinkappRole> selectRolesByUser(LinkappUser user) {
		if(user==null || user.getId()==null){
			return null;
		}
		return linkappRoleMapper.findByUsersId(user.getId());
	}
}
